#include "DX11ShaderManager.h"
#include "ObjMeshManager.h"
#include "ScreenManager.h"
#include "LightingManager.h"
#include "DX11LightingManager.h"

// shader input form
// mesh draws etc
D3D11_INPUT_ELEMENT_DESC layout[] =
{
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 44, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

// Define the input layout

static UINT numElements = ARRAYSIZE( layout );

DX11ShaderManager::DX11ShaderManager()
{
	//m_pVertexShader = NULL;
	//m_pPixelShader = NULL;
	//m_pVertexLayout = NULL;
}

HRESULT DX11ShaderManager::CompileShaderFromFile(std::string shaderName, WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
{
    HRESULT hr = S_OK;

    DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

    ID3DBlob* pErrorBlob;
    hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel, 
        dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
    if( FAILED(hr) )
    {
        if( pErrorBlob != NULL )
            OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
        if( pErrorBlob ) pErrorBlob->Release();
        return hr;
    }
    if( pErrorBlob ) pErrorBlob->Release();

	//m_vShaders[shaderName] = 

    return S_OK;
}

HRESULT DX11ShaderManager::LoadShader(std::string shaderName, const std::wstring& filename, const LPCSTR& psFuncName, const LPCSTR& vsFuncName)
{
	HRESULT hr = S_OK;

	// Compile the pixel shader
	ID3DBlob* pPSBlob = NULL;
	// add pixel shader filename + id3dblob* to shader manager
	

	std::wstring finalFilePath = L"../../DX11Shaders/" + filename;

	WCHAR* fn = const_cast<WCHAR*>(finalFilePath.c_str());

	hr = TheDX11ShaderManager::Instance()->CompileShaderFromFile("TutShad", fn, psFuncName, "ps_4_0", &pPSBlob);
    if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
        return hr;
    }

	// Create the pixel shader
	hr = TheScreenManager::Instance()->GetDevice()->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &m_pShaders[shaderName]);
	pPSBlob->Release();
    if( FAILED( hr ) )
        return hr;

	
    // Compile the vertex shader
    ID3DBlob* pVSBlob = NULL;

	hr = TheDX11ShaderManager::Instance()->CompileShaderFromFile("TutShad", fn, vsFuncName, "vs_4_0", &pVSBlob);
    if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
        return hr;
    }

	// Create the vertex shader
	hr = TheScreenManager::Instance()->GetDevice()->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_vShaders[shaderName]);
	if( FAILED( hr ) )
	{	
		pVSBlob->Release();
        return hr;
	}
	
    // Create the input layout
	hr = TheScreenManager::Instance()->GetDevice()->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
		pVSBlob->GetBufferSize(), &TheObjMeshManager::Instance()->m_pVertexLayout );
	pVSBlob->Release();
	if(FAILED(hr))
        return hr;

	
    // Set the input layout
	TheScreenManager::Instance()->GetDeviceContext()->IASetInputLayout(TheObjMeshManager::Instance()->GetVertLayout());

	return hr;
}
	
std::pair<ID3D11VertexShader*, ID3D11PixelShader*>& DX11ShaderManager::UseShader(std::string shaderName)
{
	return std::pair<ID3D11VertexShader*, ID3D11PixelShader*>(m_vShaders[shaderName], m_pShaders[shaderName]);
}


ID3D11VertexShader* DX11ShaderManager::GetVertShader(std::string shaderName)
{
	return m_vShaders.find(shaderName)->second;
	//return m_vShaders[shaderName];
}

ID3D11PixelShader* DX11ShaderManager::GetPixShader(std::string shaderName)
{
	return m_pShaders.find(shaderName)->second;
	//return m_pShaders[shaderName];
}

bool DX11ShaderManager::ReleaseShaders()
{

	//return true;
	for (std::map<std::string, ID3D11VertexShader*>::iterator it = m_vShaders.begin(); it != m_vShaders.end(); ++it)
	{
		if(it->second)
		{
			it->second->Release();
		}
	}
	for (std::map<std::string, ID3D11PixelShader*>::iterator it2 = m_pShaders.begin(); it2 != m_pShaders.end(); ++it2)
	{
		if (it2->second)
		{
			it2->second->Release();
		}
	}

	m_vShaders.clear();
	m_pShaders.clear();


	return true;
}

void DX11ShaderManager::ResetShaders()
{
	for (std::map<std::string, ID3D11VertexShader*>::iterator it = m_vShaders.begin();
		it != m_vShaders.end(); ++it)
	{
		
	}

}

void DX11ShaderManager::ResetResources()
{
	
	//ID3D11Buffer* screenCb = TheScreenManager::Instance()->GetConstantBuffer();
	ConstantBuffer cb;
	D3DXMatrixIdentity(&cb.m_world);
	D3DXMatrixTranspose(&cb.m_world, &cb.m_world);
	D3DXMatrixTranspose(&cb.m_view, &TheScreenManager::Instance()->GetViewMatrix());
	D3DXMatrixTranspose(&cb.m_projection, &TheScreenManager::Instance()->GetViewMatrix());

	D3DXMatrixTranspose(&cb.m_lightViewMatrix, &TheLightingManager::Instance()->GetLightViewMatrix());
	D3DXMatrixTranspose(&cb.m_lightProjMatrix, &TheLightingManager::Instance()->GetLightProjMatrix());

	TheScreenManager::Instance()->GetDeviceContext()->PSSetShader(NULL, NULL, 0);
	//TheScreenManager::Instance()->GetDeviceContext()->PSSetSamplers(0, 1, NULL);

	ID3D11ShaderResourceView* tex = NULL;
	TheScreenManager::Instance()->GetDeviceContext()->PSSetShaderResources(0, 1, &tex);
	TheScreenManager::Instance()->GetDeviceContext()->PSSetShaderResources(1, 1, &tex);
}